#include <kernel.h>

spin_lock_t dk_thread_lock;
struct dde_kit_thread dk_threads[DK_THREAD_COUNT];

/**
 * Create thread
 *
 * \param   fun   thread function
 * \param   arg   argument to thread function, set to NULL if not needed
 * \param   name  thread name
 *
 * \return  thread handle
 *
 * Create a new thread running the specified function with argument arg. The
 * thread is assigned the given name.
 *
 * All DDE kit threads support thread-local storage where one data pointer may
 * be stored and retrieved.
 */
struct dde_kit_thread *
LD_SYM(dde_kit_thread_create)(void (*fun)(void *), void *arg, const char *name)
{
	 uint32_t flags = spl_acquire_irq_save(&dk_thread_lock);

	 int i;
	 for (i = 0; i != DK_THREAD_COUNT; ++ i)
		  if (dk_threads[i].free) break;
	 if (i == DK_THREAD_COUNT) return NULL;

	 struct dde_kit_thread *result = dk_threads + i;
	 result->free = 0;
	 
	 spl_release_irq_restore(&dk_thread_lock, flags);
	 
	 strncpy(result->name, name, 20);
	 proc_t p = kthread_create("__dk_thread", 0, 0, (void(*)(void *))fun, arg, 0);
	 result->proc_id = (int)(((uintptr_t)p) >> 2);
	 proc_attach(p);
	 return result;
}

/**
 * Adopt calling as DDE kit thread
 *
 * \param   name  thread name
 *
 * \return  thread handle
 */
struct dde_kit_thread *
LD_SYM(dde_kit_thread_adopt_myself)(const char *name)
{
	 uint32_t flags = spl_acquire_irq_save(&dk_thread_lock);

	 int i;
	 for (i = 0; i != DK_THREAD_COUNT; ++ i)
		  if (dk_threads[i].free) break;
	 if (i == DK_THREAD_COUNT) return NULL;

	 struct dde_kit_thread *result = dk_threads + i;
	 result->proc_id = ((uintptr_t)proc_self()) >> 2;
	 spl_release_irq_restore(&dk_thread_lock, flags);

	 result->free = 0;
	 strncpy(result->name, name, 20);

	 return result;
}

/**
 * Get handle of current thread
 *
 * \return  thread handle
 */
struct dde_kit_thread *
LD_SYM(dde_kit_thread_myself)(void)
{
	 int i;
	 int proc_id = ((uintptr_t)proc_self()) >> 2;
	 
	 for (i = 0; i != DK_THREAD_COUNT; ++ i)
	 {
		  if (!dk_threads[i].free && dk_threads[i].proc_id == proc_id)
		  {
			   return dk_threads + i;
		  }
	 }

	 /* ekf_kprintf("ERROR in myself proc %08x!\n", proc_self()); */
	 /* print_backtrace(); */
	 return NULL;
}

/**
 * Get thread-local data of a specific thread
 *
 * \param   thread  thread handle
 *
 * \return  thread-local data of this thread
 */
void *
LD_SYM(dde_kit_thread_get_data)(struct dde_kit_thread * thread)
{
	 return thread->data;
}

/**
 * Get thread-local data of current thread
 *
 * \return  thread-local data of current thread
 */
void *
LD_SYM(dde_kit_thread_get_my_data)(void)
{
	 return LD_SYM(dde_kit_thread_get_data)(LD_SYM(dde_kit_thread_myself)());
}

/**
 * Set thread-local data of specific thread
 *
 * \param   thread  thread handle
 * \param   data    thread-local data pointer
 */
void
LD_SYM(dde_kit_thread_set_data)(struct dde_kit_thread *thread, void *data)
{
	 thread->data = data;
}

/**
 * Set thread-local data of current thread
 *
 * \param   data  thread-local data pointer
 */
void
LD_SYM(dde_kit_thread_set_my_data)(void *data)
{
	 LD_SYM(dde_kit_thread_set_data)(LD_SYM(dde_kit_thread_myself)(), data);
}

/**
 * Sleep (milliseconds)
 *
 * \param   msecs time to sleep in milliseconds
 */
void
LD_SYM(dde_kit_thread_msleep)(unsigned long msecs)
{
	 uint32_t dl = *hpet_tick + (uint32_t)(hpet_tick_freq / 1000.0 * msecs);
	 while ((int32_t)(dl - *hpet_tick) > 0) ;
}

/**
 * Sleep (microseconds)
 *
 * \param   usecs time to sleep in microseconds
 */
void
LD_SYM(dde_kit_thread_usleep)(unsigned long usecs)
{
	 uint32_t dl = *hpet_tick + (uint32_t)(hpet_tick_freq / 1000000.0 * usecs);
	 while ((int32_t)(dl - *hpet_tick) > 0) ;
}

/**
 * Sleep (nanoseconds)
 *
 * \param   nsecs time to sleep in nanoseconds
 */
void
LD_SYM(dde_kit_thread_nsleep)(unsigned long nsecs)
{
	 uint32_t dl = *hpet_tick + (uint32_t)(hpet_tick_freq / 1000000000.0 * nsecs);
	 while ((int32_t)(dl - *hpet_tick) > 0) ;
}

/**
 * Exit current thread
 */
void
LD_SYM(dde_kit_thread_exit)(void)
{
	 proc_exit();
}

/**
 * Get thread name
 *
 * \param   thread  thread handle
 */
const char *
LD_SYM(dde_kit_thread_get_name)(struct dde_kit_thread *thread)
{
	 return thread->name;
}

/**
 * Get unique ID
 *
 * \param   thread  thread handle
 * \return  artificial thread ID
 *
 * DDE kit does not allow direct access to the thread data structure, since
 * this struct contains platform-specific data types. However, applications
 * might want to get some kind of ID related to a dde_kit_thread, for instance
 * to use it as a Linux-like PID.
 *
 * XXX This function may be removed.
 */
int
LD_SYM(dde_kit_thread_get_id)(struct dde_kit_thread *thread)
{
	 return thread->proc_id;
}

/**
 * Hint that this thread is done and may be scheduled somehow
 */
void
LD_SYM(dde_kit_thread_schedule)(void)
{
	 proc_yield();
}

#if DK_EXPORT_SYM

void
dde_kit_thread_symbol(void)
{
	 elf_mod_export_symbol("dde_kit_thread_create", &dde_kit_thread_create, 1);
	 elf_mod_export_symbol("dde_kit_thread_adopt_myself", &dde_kit_thread_adopt_myself, 1);
	 elf_mod_export_symbol("dde_kit_thread_myself", &dde_kit_thread_myself, 1);
	 elf_mod_export_symbol("dde_kit_thread_get_data", &dde_kit_thread_get_data, 1);
	 elf_mod_export_symbol("dde_kit_thread_get_my_data", &dde_kit_thread_get_my_data, 1);
	 elf_mod_export_symbol("dde_kit_thread_set_data", &dde_kit_thread_set_data, 1);
	 elf_mod_export_symbol("dde_kit_thread_set_my_data", &dde_kit_thread_set_my_data, 1);
	 elf_mod_export_symbol("dde_kit_thread_msleep", &dde_kit_thread_msleep, 1);
	 elf_mod_export_symbol("dde_kit_thread_usleep", &dde_kit_thread_usleep, 1);
	 elf_mod_export_symbol("dde_kit_thread_nsleep", &dde_kit_thread_nsleep, 1);
	 elf_mod_export_symbol("dde_kit_thread_exit", &dde_kit_thread_exit, 1);
	 elf_mod_export_symbol("dde_kit_thread_get_name", &dde_kit_thread_get_name, 1);
	 elf_mod_export_symbol("dde_kit_thread_get_id", &dde_kit_thread_get_id, 1);
	 elf_mod_export_symbol("dde_kit_thread_schedule", &dde_kit_thread_schedule, 1);
}

#endif
